package com.github.monkeywie.proxyee.log;


import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import com.github.monkeywie.proxyee.log.LogRecorder.Level;


/**
 * 记录器工厂
 * @author MenglongLi
 *
 */
public class LogRecorderFactory {
	public static final LogRecorder devNullLogRecorder = new LogRecorder();
	public static final LogRecorder consoleLogRecorder = new LogRecorder(System.out);
	/**
	 * 类注册日志记录器，只能当前类使用
	 */
	private static final Map<Class<? extends Object>, LogRecorderWrapper> classRegisterLogRecorder = new ConcurrentHashMap<Class<? extends Object>, LogRecorderWrapper>();
	/**
	 * 包名注册的日志记录器，可继承来自父包的记录器
	 */
	private static final Map<String, LogRecorderWrapper> packageRegisterLogRecorder = new ConcurrentHashMap<String, LogRecorderWrapper>();

	private LogRecorderFactory() {
	}

	/**
	 * 合并classRegisterLogRecorder与packageRegisterLogRecorder，获取属于当前类的记录器;
	 * 若未找到记录器时返回consoleLogRecorder记录器;
	 * @param clazz
	 * @return
	 */
	public static LogRecorder getRecorder(Class<?> clazz){
		LogRecorderWrapper logRecorder=new LogRecorderWrapper(new HashSet<LogRecorder>());
		if(classRegisterLogRecorder.containsKey(clazz)){
			logRecorder.add(classRegisterLogRecorder.get(clazz));
		}
		String packageName=clazz.getPackage().getName();
		
		while(!packageRegisterLogRecorder.containsKey(packageName) && packageName.lastIndexOf(".")!=-1){
			packageName=packageName.substring(0,packageName.lastIndexOf("."));
		}
		
		if(packageRegisterLogRecorder.containsKey(packageName)){
			logRecorder.add(packageRegisterLogRecorder.get(packageName));
		}
		
		if(packageRegisterLogRecorder.containsKey("ALL")){
			logRecorder.add(packageRegisterLogRecorder.get("ALL"));
		}
		
		if(logRecorder.recorderSet.size()==0){
			return consoleLogRecorder;
		}
		return logRecorder;
	}

	public static void register(Class<?> clazz,Set<LogRecorder> recorderSet){
		if(classRegisterLogRecorder.containsKey(clazz)){
			classRegisterLogRecorder.get(clazz).recorderSet.addAll(recorderSet);
		}else{
			classRegisterLogRecorder.put(clazz, new LogRecorderWrapper(recorderSet));
		}
	}
	
	public static void register(Class<?> clazz,LogRecorder recoder){
		Set<LogRecorder> recorderSet=new HashSet<LogRecorder>();
		recorderSet.add(recoder);
		register(clazz,recorderSet);
	}
	
	/**
	 * 注册记录器
	 * @param packageName 包名,其中"ALL"为默认根包名
	 * @param recorderSet 记录器集合
	 */
	public static void register(String packageName,Set<LogRecorder> recorderSet){
		LogRecorderWrapper currectLogRecorderWrapper=null;
		if(packageRegisterLogRecorder.containsKey(packageName)){
			currectLogRecorderWrapper=packageRegisterLogRecorder.get(packageName);
			currectLogRecorderWrapper.recorderSet.addAll(recorderSet);
		}else{
			currectLogRecorderWrapper=new LogRecorderWrapper(recorderSet);
			packageRegisterLogRecorder.put(packageName,currectLogRecorderWrapper);
		}
		
		for(String pName:packageRegisterLogRecorder.keySet()){
			if(packageName.equals(pName)){
				continue;
			}
			if(pName.equals("ALL")||packageName.startsWith(pName+".")){
				currectLogRecorderWrapper.add(packageRegisterLogRecorder.get(pName));
			}
			if(pName.startsWith(packageName+".")){
				packageRegisterLogRecorder.get(pName).add(currectLogRecorderWrapper);
			}
		}
		
	}
	
	/**
	 * 注册记录器
	 * @param packageName 包名,其中"ALL"为默认根包名
	 * @param recoder 记录器
	 */
	public static void register(String packageName,LogRecorder recoder){
		Set<LogRecorder> recorderSet=new HashSet<LogRecorder>();
		recorderSet.add(recoder);
		register(packageName,recorderSet);
	}
	
	
	/**
	 * 构建文件记录器，默认为完整格式
	 * @param file
	 * @param append
	 * @return
	 */
	public static LogRecorder buildFileLogRecorder(File file,boolean append){
		try {
			LogRecorder fileRecorder = new LogRecorder(new FileOutputStream(file,append));
			fileRecorder.setContentFormatFun(buildFullContentFormatFun());
			return fileRecorder;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		return devNullLogRecorder;
	}
	


	/**
	 * 简洁的内容格式化函数
	 * @return
	 */
	public static BiFunction<Level, String, String> buildSimpleContentFormatFun(){
		return new BiFunction<Level,String,String>(){
			private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
			@Override
			public String apply(Level level,String content) {
				return LocalDateTime.now().format(dtf)+"\t["+level.name()+"]\t : "+content+System.getProperty("line.separator");
			}
		};
	}

	/**
	 * 完整的内容格式化函数
	 * @return
	 */
	public static BiFunction<Level, String, String> buildFullContentFormatFun(){
		return new BiFunction<Level, String, String> (){
			private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
			@Override
			public String apply(Level level, String content) {
				StackTraceElement stack=getInvokeLogTraceElement();
				if(stack!=null){
					String invokeInfo=stack.getClassName()+"."+stack.getMethodName()+"("+stack.getLineNumber()+")";
					return LocalDateTime.now().format(dtf)+"\t["+level.name()+"] ["+Thread.currentThread().getName()+"]\t"+invokeInfo+"\t : "+content+System.getProperty("line.separator");
				}else{
					return LocalDateTime.now().format(dtf)+"\t["+level.name()+"] ["+Thread.currentThread().getName()+"]\t : "+content+System.getProperty("line.separator");
				}
			}
		};
	}
	
	
	/**
	 * 获取堆栈信息
	 * @return
	 */
	private static StackTraceElement getInvokeLogTraceElement(){
		StackTraceElement stack[] = Thread.currentThread().getStackTrace();
		for(int i=0;i<stack.length;i++){
			StackTraceElement element=stack[i];
			if(element.getClassName().equals(LogRecorder.class.getName()) && ("trace".equals(element.getMethodName())||"debug".equals(element.getMethodName())||"info".equals(element.getMethodName())||"warn".equals(element.getMethodName())||"error".equals(element.getMethodName())||"fatal".equals(element.getMethodName()))){
				if(i<stack.length-1){
					return stack[i+1];
				}
			}
		}
		return null;
	}

	/**
	 * 彩色字体的内容格式化函数
	 * @return
	 */
	public static BiFunction<Level, String, String> buildMultiColourContentFormatFun(){
		return new BiFunction<Level, String, String> (){
			private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
			@Override
			public String apply(Level level, String content) {
				if(level==Level.FATAL) {
					return LocalDateTime.now().format(dtf) + "\t[" + styleDecorate(level.name(), AnsiEscape.BG_RED) + "]\t : " + styleDecorate(content, AnsiEscape.BG_RED) + System.getProperty("line.separator");
				}else if(level==Level.ERROR){
					return LocalDateTime.now().format(dtf)+"\t["+styleDecorate(level.name(),AnsiEscape.RED)+"]\t : "+styleDecorate(content,AnsiEscape.RED)+System.getProperty("line.separator");
				}else if(level==Level.WARN){
					return LocalDateTime.now().format(dtf)+"\t["+styleDecorate(level.name(),AnsiEscape.MAGENTA)+"]\t : "+styleDecorate(content,AnsiEscape.MAGENTA)+System.getProperty("line.separator");
				}else if(level==Level.INFO){
					return LocalDateTime.now().format(dtf)+"\t["+styleDecorate(level.name(),AnsiEscape.BLUE)+"]\t : "+styleDecorate(content,AnsiEscape.BLUE)+System.getProperty("line.separator");
				}else if(level==Level.DEBUG){
					return LocalDateTime.now().format(dtf)+"\t["+styleDecorate(level.name(),AnsiEscape.GREEN)+"]\t : "+styleDecorate(content,AnsiEscape.GREEN)+System.getProperty("line.separator");
				}else {
					return LocalDateTime.now().format(dtf)+"\t["+styleDecorate(level.name(),AnsiEscape.YELLOW)+"]\t : "+styleDecorate(content,AnsiEscape.YELLOW)+System.getProperty("line.separator");
				}
			}
		};
	}
	/**
	 * 样式转换
	 * @param content
	 * @param escapes
	 * @return
	 */
	private static String styleDecorate(String content,AnsiEscape...escapes) {
		if(escapes!=null && escapes.length>0){
			StringBuilder sb=new StringBuilder();
			for(AnsiEscape escape:escapes){
				sb.append(escape.escape);
			}
			sb.append(content).append(AnsiEscape.CLOSE.escape);
			return sb.toString();
		}else {
			return content;
		}
	}

	/**
	 * 完整内容的彩色字体的内容格式化函数
	 * @return
	 */
	public static BiFunction<Level, String, String> buildMultiColourFullContentFormatFun(){
		BiFunction<Level, String, String> colourContentFormatFun=buildMultiColourContentFormatFun();
		return new BiFunction<Level, String, String> (){
			@Override
			public String apply(Level level, String content) {
				StackTraceElement stack=getInvokeLogTraceElement();
				if(stack!=null){
					String invokeInfo=stack.getClassName()+"."+stack.getMethodName()+"("+stack.getLineNumber()+")";
					content = "["+Thread.currentThread().getName()+"]\t"+invokeInfo+"\t : "+content;
				}else{
					content = "["+Thread.currentThread().getName()+"]\t : "+content;
				}
				return colourContentFormatFun.apply(level,content);
			}
		};
	}

	/**
	 * 可更换的内容函数(光标始终处于开始位置，清除光标后的内容然后输出新的内容)
	 * @return
	 */
	public static BiFunction<Level, String, String> buildReplaceContentFormatFun(){

		AtomicInteger n=new AtomicInteger(0);
		return (level,content)->{
			String lineSeparator = System.getProperty("line.separator");
			StringBuilder sb=new StringBuilder();
			sb.append("\033["+n.get()+"A\033[J").append(content).append(lineSeparator);
			n.set((content+lineSeparator).split(lineSeparator).length);
			return sb.toString();
			/*StringBuilder sb=new StringBuilder();
			sb.append(AnsiEscape.CURSOR_SAVE.escape);
			sb.append(AnsiEscape.CLEAN_AFTER.escape);
			sb.append(content);
			sb.append(System.getProperty("line.separator"));
			sb.append(AnsiEscape.CURSOR_RECOVER.escape);
			return sb.toString();*/
		};


	}



	/**
	 * 按照默认配置初始化记录器,全局配置两个文件记录器及一个控制台记录器；
	 * 文件记录器位于程序运行的目录，名称为"app.log"和"error.log",分别记录完整日志及错误日志。
	 * @param isDebug 是否将记录器设为DEBUG级别
	 */
	public static void commonlyInit(boolean isDebug){
		File logDir=new File("logs");
		if(!logDir.exists()){
			logDir.mkdirs();
		}
		LogRecorder fileRecorder1 = LogRecorderFactory.buildFileLogRecorder(new File(logDir,"app.log"), true);
		LogRecorder fileRecorder2 = LogRecorderFactory.buildFileLogRecorder(new File(logDir,"error.log"), true);
		fileRecorder2.setLowestLevel(Level.ERROR);
		if(isDebug){
			fileRecorder1.setLowestLevel(Level.DEBUG);
			LogRecorderFactory.consoleLogRecorder.setLowestLevel(Level.DEBUG);
		}
		LogRecorderFactory.register("ALL", fileRecorder1);
		LogRecorderFactory.register("ALL", fileRecorder2);
		LogRecorderFactory.register("ALL", LogRecorderFactory.consoleLogRecorder);
	}
	
	
	static class LogRecorderWrapper extends LogRecorder{
		Set<LogRecorder> recorderSet=new HashSet<LogRecorder>();
		public LogRecorderWrapper(Set<LogRecorder> recorderSet){
			this.recorderSet=recorderSet;
			resetLowestLevel(recorderSet);
		}
		
		public void add(LogRecorderWrapper logRecorderWrapper){
			this.recorderSet.addAll(logRecorderWrapper.recorderSet);
			resetLowestLevel(logRecorderWrapper.recorderSet);
		}

		/**
		 * 检查/重设log级别
		 * @param recorderSet
		 */
		private void resetLowestLevel(Set<LogRecorder> recorderSet){
			recorderSet.forEach(recorder->{
				if(this.lowestLevel.getCode()>recorder.lowestLevel.getCode()){
					this.lowestLevel=recorder.lowestLevel;
				}
			});
		}

		@Override
		public void setLowestLevel(Level lowestLevel) {
			this.lowestLevel=lowestLevel;
			recorderSet.forEach(recorder->recorder.setLowestLevel(lowestLevel));
		}
		@Override
		public void setContentFormatFun(BiFunction<Level, String, String> contentFormatFun) {
			this.contentFormatFun=contentFormatFun;
			recorderSet.forEach(recorder->recorder.setContentFormatFun(contentFormatFun));
		}

		@Override
		public void log(Level level,String content,Object...objects) {
			for(LogRecorder recorder:recorderSet){
				recorder.log(level, content,objects);
			}
		}

		@Override
		public OutputStream getOutput(){
			Set<OutputStream> outputStreams=new HashSet<>();
			for(LogRecorder recorder:recorderSet){
				outputStreams.add(recorder.getOutput());
			}
			return new OutputStream() {
				@Override
				public void write(int b) throws IOException {
					for(OutputStream outputStream:outputStreams){
						outputStream.write(b);
					}
				}
				@Override
				public void write(byte b[]) throws IOException {
					for(OutputStream outputStream:outputStreams){
						outputStream.write(b);
					}
				}
				@Override
				public void write(byte b[], int off, int len) throws IOException {
					for(OutputStream outputStream:outputStreams){
						outputStream.write(b,off,len);
					}
				}
				@Override
				public void flush() throws IOException {
					for(OutputStream outputStream:outputStreams){
						outputStream.flush();
					}
				}
				@Override
				public void close() throws IOException {
					for(OutputStream outputStream:outputStreams){
						outputStream.close();
					}
				}
			};
		}
	}
	
	private static enum AnsiEscape{
		// @formatter:off
		/*
		 * ANSI escape code,控制文本颜色的ANSI转义序列。
		 * 通用的控制文本颜色的转义序列格式如下： CSI n1 [;n2 [;…]] m
		 * 其中CSI全称为“控制序列引导器”（Control Sequence Introducer/Initiator），也就是上述示例中的"\033["；
		 * n1、n2等表示SGR参数，用于控制颜色、粗体、斜体、闪烁等文本输出格式；
		 * m表示转义序列结束。
		 * 
		 * 常用的SRG参数列表如下：
		 * 0 	关闭所有格式，还原为初始状态
		 * 1 	粗体/高亮显示
		 * 2 	模糊（※）
		 * 3 	斜体（※）
		 * 4 	下划线（单线）
		 * 5 	闪烁（慢）
		 * 6 	闪烁（快）（※）
		 * 7 	交换背景色与前景色
		 * 8 	隐藏（伸手不见五指，啥也看不见）（※）
		 * 30-37 	前景色，即30+x，x表示不同的颜色（参见下面的“颜色表”)
		 * 40-47 	背景色，即40+x，x表示不同的颜色（参见下面的“颜色表”） 
		 * 
		 * 颜色值x 	0 	1 	2 	3 	4 	5 	6 	7
		 * 颜色 		黑 	红 	绿 	黄 	蓝 	紫 	青 	白
		 */	
		
		// @formatter:on
		
		BLACK("\033[30m"), 	//黑色字体
		RED("\033[31m"), 	//红色字体
		GREEN("\033[32m"),	//绿色字体
		YELLOW("\033[33m"),	//黄色字体
		BLUE("\033[34m"),	//蓝色字体
		MAGENTA("\033[35m"),	//洋红色字体
		CYAN("\033[36m"),	//青色字体
		WHILE("\033[37m"),	//白色字体

		BG_BLACK("\033[40m"), //黑色背景
		BG_RED("\033[41m"), 	//红色背景
		BG_GREEN("\033[42m"),	//绿色背景
		BG_YELLOW("\033[43m"),	//黄色背景
		BG_BLUE("\033[44m"),	//蓝色背景
		BG_MAGENTA("\033[45m"),	//洋红色背景
		BG_CYAN("\033[46m"),	//青色背景
		BG_WHILE("\033[47m"),	//白色背景

		CLOSE("\033[0m"),	//关闭所有格式

		//屏幕控制
		CURSOR_SAVE("\033[s"), //保存光标
		CURSOR_RECOVER("\033[u"),	//恢复光标位置
		CLEAN_AFTER("\033[J"), //清除光标之后的内容

		;	

		private String escape;
		
		private AnsiEscape(String escape) {
			this.escape=escape;
		}
		
	}
	
}
